
"""
test the usage of string in Python
"""

import unittest
import string

class TestString(unittest.TestCase):
    
    def test_length(self):
        self.assertEqual(5,len("cheka"))
        
    def test_slice(self):
        name = "cheka"
        
        # slice means "[startindex:endindex)", that means it will include element at "endindex-1"
        # but exclude the element at position "endindex"
        self.assertEqual("ch",name[:2])
        self.assertEqual("eka",name[2:])
        self.assertEqual("e",name[2:3])
        
        self.assertEqual("cea",name[::2])
        self.assertEqual("akehc",name[::-1])
    
    def test_index(self):
        name = "cheka"
        expected = ["c","h","e","k","a"]
        
        for index in range(len(name)):
            self.assertEqual(expected[index],name[index])
            
        self.assertEqual("c",name[-5])
        self.assertEqual("a",name[-1])
            
    def test_multiple(self):
        self.assertEqual("chekacheka","cheka" * 2)
        
    def test_reverse_by_slice(self):
        name = "cheka"
        
        # start-index is default, end-index is default, but step is -1, which means from end-->start
        self.assertEqual("akehc",name[::-1])
        
        # still reverse, but filter by every two characters
        self.assertEqual("aec",name[::-2])
        
    def test_replace(self):
        self.assertEqual("chxxa","cheka".replace("ek","xx"))
        
        # replace accepts a third argument, indicating how many subsitution will occur
        self.assertEqual("1 2 3","1x2x3".replace("x"," "))
        self.assertEqual("1 2x3","1x2x3".replace("x"," ",1))
        
    def test_str_and_repr(self):
        arecord = [1,"cheka",100.0]
        expected = "[1, 'cheka', 100.0]"
        
        self.assertEqual(expected,str(arecord))
        self.assertEqual(expected,repr(arecord))
        self.assertEqual(expected,`arecord`)
        
    def test_ord_chr(self):
        ori_character = "a"
        self.assertEqual(ori_character,chr(ord(ori_character) ))
        
    def test_compare(self):
        # upper characters are always smaller than lower characters
        self.assertTrue("Z" < "a")
        self.assertTrue(cmp("abc","xyz") < 0)
        self.assertTrue(cmp("abc","XYZ") > 0)
        
    def test_in(self):
        """
        since Python 2.3, substring with multiple characters can be used as the first parameter of 'in'
        different from List, a sublist cannot use "in" to check whether it is part of the whole list
        """
        name = "cheka"
        self.assertTrue("cheka" in "cheka")
        self.assertTrue("he" in "cheka")
        self.assertTrue("eka" in "cheka")
        
    def test_string_constants(self):
        self.assertEqual("ABCDEFGHIJKLMNOPQRSTUVWXYZ",string.uppercase)
        self.assertEqual("abcdefghijklmnopqrstuvwxyz",string.lowercase)
        self.assertEqual("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",string.letters)
        self.assertEqual("0123456789",string.digits)
        
    def test_format_string(self):
        formated_string = "hello %s from %s for %d times"%("wsu","cheka",10)
        self.assertEqual("hello wsu from cheka for 10 times",formated_string)
        
        # new format string in Python2.6 and Python3.0
        formated_string = "{0} {1} in {2}".format("hello","wsu",2010)
        self.assertEqual("hello wsu in 2010",formated_string)
        
    def test_template(self):
        greeting_template = string.Template("hello ${target} from ${source} for ${number} times")
        self.assertEqual("hello wsu from cheka for 4 times",greeting_template.substitute(target="wsu",source="cheka",number=4))
        
        # missing key "number"
        self.assertRaises(KeyError,lambda : greeting_template.substitute(target="wsu",source="cheka"))
        
        # safe_subsitute won't throw exception when key is missed
        self.assertEqual("hello wsu from cheka for ${number} times",greeting_template.safe_substitute(target="wsu",source="cheka"))
        
    def test_join(self):
        names = ["cheka","stasi","kgb"]
        self.assertEqual("cheka,stasi,kgb",",".join(names))
        self.assertEqual("cheka stasi kgb"," ".join(names))
        self.assertEqual("chekastasikgb","".join(names))
        
    def test_compiletime_concatenate(self):
        concatenated = "cheka""wsu""lucky"
        self.assertEqual("chekawsulucky",concatenated)
    
    def test_raw_string(self):
        self.assertEqual("\\n",r"\n")
        
    def test_count(self):
        astring = "chekacheka"
        self.assertEqual(2,astring.count("ch"))
        
    def test_join_split(self):
        names = ("cheka","mss","kgb","stasi")
        # list cannot equal with tuple, even their contents are totally equivalent
        self.assertEqual(list(names),string.split(" ".join(names)))
        
    def test_split(self):
        msg = "hello, world"
        self.assertEqual(["hello,","world"],msg.split()) # default seperator is whitespace
        self.assertEqual(["hello"," world"],msg.split(","))
        
    def test_find(self):
        name = "cheka"
        self.assertTrue(name.endswith("ka"))
        self.assertTrue(name.startswith("che"))
        
        self.assertEqual(2,name.find("ek"))
        self.assertEqual(-1,name.find("xy"))
        
        # "index" works just like "find", but when not found, instead returning -1 as find, throw an exception
        self.assertEqual(1,name.index("he"))
        self.assertRaises(ValueError, lambda : name.index("xy") )
        
    def test_strip(self):
        """
        strip can not only remove white spaces, but can also remove line mark
        """
        name = "  cheka  \n"
        self.assertEqual("cheka",name.strip())
        self.assertEqual("cheka  \n",name.lstrip())
        self.assertEqual("  cheka",name.rstrip())

if __name__ == "__main__":
    unittest.main()